package tables

import (
	"fmt"
	"peilian-api/app/global/variable"
	"time"

	"gorm.io/gorm"
)

// 用户类型
const (
	UserTypeSadmin     string = "sadmin"     // 超级用户
	UserTypeAdmin      string = "admin"      // 管理员
	UserTypeNormal     string = "normal"     // 普通用户
	UserTypeAnonymous  string = "anonymous"  // 游客用户
	UserTypeSpecial    string = "special"    // 游客管理员-特殊
	UserTypeEnterprise string = "enterprise" // 企业用户
	UserTypeTeacher    string = "teacher"    // 教师身份

	MemberStatusDefault = 0
	MemberStatusHas     = 1
	MemberStatusExpire  = 2
)

var StafArr = []string{"游客", "学生"}

var StafMapArr = map[string]string{"游客": UserTypeAnonymous, "学生": UserTypeNormal}

var UseTypeMap = map[string]string{UserTypeAnonymous: "游客", UserTypeNormal: "学生"}

var AdminArr = []string{UserTypeSadmin, UserTypeAdmin, UserTypeSpecial}

type User struct { //用户表
	Model
	Sno         string  `json:"sno" gorm:"type:varchar(16);not null;default:\"\";comment:学号"`
	Name        string  `json:"name" gorm:"type:varchar(50);not null;comment:姓名"`
	Password    string  `json:"password,omitempty" gorm:"type:varchar(255);comment:用户密码"`
	AccountName *string `json:"account_name,omitempty" gorm:"type:varchar(50);comment:账户"`
	Type        string  `json:"type,omitempty" gorm:"type:varchar(20);comment:用户类型,sadmin/admin/normal/anonymous"`

	//以下信息根据需要存储
	School     string `json:"school" gorm:"type:varchar(100);comment:学校"`
	Academy    string `json:"academy" gorm:"type:varchar(255);comment:学院"`
	Profession string `json:"profession" gorm:"type:varchar(255);comment:专业"`
	Grade      string `json:"grade" gorm:"type:varchar(20);comment:年级"`
	Education  string `json:"education" gorm:"type:varchar(20);comment:学历"`

	//上面的信息可以根据构成外键来从其他表中获取
	ConstituteID uint       `json:"-"`
	Constitute   Constitute `json:"-"`

	AgentID uint  `json:"-" gorm:"comment:客户信息表外键"`
	Agent   Agent `json:"-"`

	Phone           string     `json:"phone" gorm:"type:varchar(20);comment:手机"`
	Email           string     `json:"mail" gorm:"type:varchar(255);comment:邮箱"`
	TrainCount      uint       `json:"-" gorm:"type:uint;default:0;comment:该用户的面试次数"`
	ShareClickCount uint       `json:"-" gorm:"type:uint;default:0;comment:该用户分享后被其他人点击的次数"`
	TrailCount      uint       `json:"-" gorm:"type:uint;default:0;comment:该用户的试用次数"`
	Token           string     `json:"token" gorm:"type:varchar(255)"`
	TokenExpire     time.Time  `json:"token_expire,omitempty"`
	LastLogin       time.Time  `json:"last_login,omitempty" gorm:"comment:最后登录时间"`
	Sessions        []*Session `json:"-" gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`

	Level        int  `json:"level" gorm:"type:tinyint(3);comment:用户等级"`
	IsChange     int  `json:"-" gorm:"type:int(3);default:0;comment:身份是否变更,1是,0无"`
	CreateUserId uint `json:"-" gorm:"default:0;comment:创建账户的user_id"`
	// 用户角色外键
	RoleId            uint      `json:"-" gorm:"comment:用户角色外键"`
	Roles             Roles     `json:"-"`
	PromoteId         string    `json:"-" gorm:"type:varchar(255);default:0;comment:推介id"`
	IconUrl           string    `json:"icon_url" gorm:"type:varchar(512);not null;default:\"\";comment:用户头像"`
	IsMember          uint      `json:"is_member" gorm:"type:tinyint(1);not null;default:0;comment:是否有会员:0无 1生效 2过期"`
	IsSync            bool      `gorm:"default:true;comment:是否开启训练记录同步"`
	IsUpdatePwd       bool      `gorm:"default:false;comment:学生第一次登录是否修改密码"`
	UseAppletsEndTime time.Time `gorm:"default:false;comment:学生使用小程序的结束时间"`

	RecommendCareer string `gorm:"comment:根据这里的标签进行岗位推荐" json:"-"`
}

func (u *User) TableName() string {
	return "users"
}

// 根据用户id获取agent详细信息
func GetAgentInfoByUserId(uid uint) (user User, err error) {
	d := variable.DB.MySQL.Where("id = ?", uid).Preload("Level").Find(&user)
	return user, d.Error
}

// 根据用户名name获取agent详细信息
func GetAgentInfoByUserName(accountName string) (user User, err error) {
	d := variable.DB.MySQL.Where("account_name = ?", accountName).Preload("Agent").Find(&user)
	return user, d.Error
}

// 根据用户名name获取agent详细信息
func GetAgentInfoByUid(uid uint) (user User, err error) {
	d := variable.DB.MySQL.Where("id = ?", uid).Preload("Agent").Find(&user)
	return user, d.Error
}

// 根据邮箱获取user详细信息
func GetUserInfoByUserEmail(email string, typs []string) (user User, err error) {
	var d *gorm.DB
	if len(typs) == 0 {
		d = variable.DB.MySQL.Where("email = ?", email).Preload("Agent").Find(&user)
	} else {
		d = variable.DB.MySQL.Where("email = ? and type in (?)", email, typs).Preload("Agent").Find(&user)
	}
	return user, d.Error
}

// 添加用户
func AddUser(user User) (int64, error) {
	d := variable.DB.MySQL.Create(&user)
	return d.RowsAffected, d.Error
}

// 批量添加用户
func BatchAddUser(users []User, batchSize int) (int64, error) {
	d := variable.DB.MySQL.CreateInBatches(users, len(users))
	if d.Error != nil {
		return 0, d.Error
	}
	return d.RowsAffected, nil
}

// 根据用户名name用户信息
func GetUserInfoByaccountName(accountName string) (user User, exists bool, err error) {
	d := variable.DB.MySQL.Where("account_name = ?", accountName).Find(&user)
	exists = true
	if d.RowsAffected == 0 {
		exists = false
	}
	return
}

// 根据手机号查询游客信息
func GetAnonymousInfoByPhone(phone string) (user User, exists bool, err error) {
	d := variable.DB.MySQL.Where("phone = ? and type = ?", phone, UserTypeAnonymous).Find(&user)
	var flag bool = true
	if d.RowsAffected == 0 {
		flag = false
	}
	return user, flag, d.Error
}

// 根据手机号获取学生信息
func GetUserInfoByPhone(phone string) (user User, exists bool, err error) {
	d := variable.DB.MySQL.Where("phone = ? and type not in (?)", phone, AdminArr).Find(&user)
	var flag bool = true
	if d.RowsAffected == 0 {
		flag = false
	}
	return user, flag, d.Error
}

// 根据学校+学号获取学生信息
func GetUserInfoBySchoolAbdSno(school, sno string) (user User, exists bool, err error) {
	d := variable.DB.MySQL.Where("school = ? and sno = ?", school, sno).Find(&user)
	var flag bool = true
	if d.RowsAffected == 0 {
		flag = false
	}
	return user, flag, d.Error
}

// 根据用户id查询用户信息
func GetUserInfoById(id int) (user User, err error) {
	d := variable.DB.MySQL.Where("id = ?", id).Find(&user)
	return user, d.Error
}

// 根据用户id更新用户信息
func UpdateUserInfoById(user User, id int, sel []string) (int64, error) {
	if len(sel) == 0 {
		d := variable.DB.MySQL.Where("id = ?", id).Updates(&user)
		return d.RowsAffected, d.Error
	}
	d := variable.DB.MySQL.Where("id = ?", id).Select(sel).Updates(&user)
	return d.RowsAffected, d.Error
}

// 根据条件更新用户信息
func (u *User) UpdateUserInfoByWhere(userMap map[string]interface{}, where map[string]interface{}) (int64, error) {
	d := variable.DB.MySQL.Model(&User{}).Where(where).Updates(userMap)
	return d.RowsAffected, d.Error
}

// 根据条件更新用户信息
func (u *User) UpdateUserInfoByWhereStr(userMap map[string]interface{}, whereStr string) (int64, error) {
	d := variable.DB.MySQL.Model(&User{}).Where(whereStr).Updates(userMap)
	return d.RowsAffected, d.Error
}

// 根据用户id更新用户信息
func UpdateMapUserInfoById(userMap map[string]interface{}, id int) (int64, error) {
	d := variable.DB.MySQL.Model(&User{}).Where("id = ?", id).Updates(userMap)
	return d.RowsAffected, d.Error
}

// 根据用户id更新用户信息
func UpdateUserInfoMapById(data map[string]interface{}, id int) (int64, error) {
	d := variable.DB.MySQL.Model(User{}).Where("id = ?", id).Updates(data)
	return d.RowsAffected, d.Error
}

// 根据用户id逻辑删除用户信息
func DeleteUserById(id int) (int64, error) {
	//注：由于软删除 + name是唯一索引，会带来插入的报错的问题，因此这里调用Update方法，将name拼接一个字符串
	var user User
	d := variable.DB.MySQL.Where("id = ?", id).Find(&user)
	if d.RowsAffected == 0 {
		return d.RowsAffected, fmt.Errorf("数据不存在，无法删除")
	}
	user.DeletedAt = gorm.DeletedAt{time.Now(), true}
	user.Name = user.Name + time.Now().Format("2006-01-02 15:04:05")
	d = variable.DB.MySQL.Where("id = ?", id).Updates(&user).Limit(1)
	return d.RowsAffected, d.Error
}

// 根据用户id逻辑删除用户信息
func DeleteUserChangeById(id int) (int64, error) {
	//注：由于软删除 + name是唯一索引，会带来插入的报错的问题，因此这里调用Update方法，将name拼接一个字符串
	var user User
	d := variable.DB.MySQL.Where("id = ?", id).Find(&user)
	if d.RowsAffected == 0 {
		return d.RowsAffected, fmt.Errorf("数据不存在，无法删除")
	}
	user.DeletedAt = gorm.DeletedAt{time.Now(), true}
	user.IsChange = 1
	user.Name = user.Name + time.Now().Format("2006-01-02 15:04:05")
	d = variable.DB.MySQL.Where("id = ?", id).Updates(&user).Limit(1)
	return d.RowsAffected, d.Error
}

// 根据条件获取用户信息
func (u *User) GetUsersByWhere(where map[string]interface{}, selectField string) ([]User, error) {
	var users []User
	if d := variable.DB.MySQL.Select(selectField).Where(where).Find(&users); d.Error != nil {
		return users, d.Error
	}
	return users, nil
}

// 根据条件获取用户信息
func (u *User) GetUsersByWhereStr(whereStr string, selectField string) ([]User, error) {
	var users []User
	if d := variable.DB.MySQL.Select(selectField).Where(whereStr).Find(&users); d.Error != nil {
		return users, d.Error
	}
	return users, nil
}

// 根据条件获取用户信息分页
func (u *User) GetUserListByWhere(whereMap map[string]interface{}, whereStr string, order string, page int, pageSize int) ([]User, int64, error) {
	var users []User
	var count int64
	if len(whereMap) != 0 && len(whereStr) != 0 && len(order) != 0 && page >= 0 && pageSize >= 0 {
		if d := variable.DB.MySQL.Model(&User{}).Where(whereMap).Where(whereStr).Offset((page-1)*pageSize).Limit(pageSize).Order(order).FindInBatches(&users, pageSize, func(tx *gorm.DB, batch int) error {
			for k, v := range users {
				var agent Agent
				var roles Roles
				if d := variable.DB.MySQL.Model(&Agent{}).Where("id = ?", v.AgentID).Find(&agent); d.Error != nil {
					return d.Error
				}
				users[k].Agent = agent
				if d := variable.DB.MySQL.Model(&Roles{}).Where("id = ?", v.RoleId).Find(&roles); d.Error != nil {
					return d.Error
				}
				users[k].Roles = roles
			}
			return nil
		}).Offset(-1).Limit(-1).Count(&count); d.Error != nil {
			return users, count, d.Error
		}
	} else if len(whereMap) != 0 && len(order) != 0 && page >= 0 && pageSize >= 0 {
		if d := variable.DB.MySQL.Model(&User{}).Where(whereMap).Offset((page-1)*pageSize).Limit(pageSize).Order(order).FindInBatches(&users, pageSize, func(tx *gorm.DB, batch int) error {
			for k, v := range users {
				var agent Agent
				var roles Roles
				if d := variable.DB.MySQL.Model(&Agent{}).Where("id = ?", v.AgentID).Find(&v.Agent); d.Error != nil {
					return d.Error
				}
				users[k].Agent = agent
				if d := variable.DB.MySQL.Model(&Roles{}).Where("id = ?", v.RoleId).Find(&v.Roles); d.Error != nil {
					return d.Error
				}
				users[k].Roles = roles
			}
			return nil
		}).Offset(-1).Limit(-1).Count(&count); d.Error != nil {
			return users, count, d.Error
		}
	} else if len(whereStr) != 0 && len(order) != 0 && page >= 0 && pageSize >= 0 {
		if d := variable.DB.MySQL.Model(&User{}).Where(whereStr).Offset((page-1)*pageSize).Limit(pageSize).Order(order).FindInBatches(&users, pageSize, func(tx *gorm.DB, batch int) error {
			for k, v := range users {
				var agent Agent
				var roles Roles
				if d := variable.DB.MySQL.Model(&Agent{}).Where("id = ?", v.AgentID).Find(&v.Agent); d.Error != nil {
					return d.Error
				}
				users[k].Agent = agent
				if d := variable.DB.MySQL.Model(&Roles{}).Where("id = ?", v.RoleId).Find(&v.Roles); d.Error != nil {
					return d.Error
				}
				users[k].Roles = roles
			}
			return nil
		}).Offset(-1).Limit(-1).Count(&count); d.Error != nil {
			return users, count, d.Error
		}
	} else if len(whereMap) != 0 && len(whereStr) != 0 {
		if d := variable.DB.MySQL.Model(&User{}).Where(whereStr).FindInBatches(&users, pageSize, func(tx *gorm.DB, batch int) error {
			for k, v := range users {
				var agent Agent
				var roles Roles
				if d := variable.DB.MySQL.Model(&Agent{}).Where("id = ?", v.AgentID).Find(&v.Agent); d.Error != nil {
					return d.Error
				}
				users[k].Agent = agent
				if d := variable.DB.MySQL.Model(&Roles{}).Where("id = ?", v.RoleId).Find(&v.Roles); d.Error != nil {
					return d.Error
				}
				users[k].Roles = roles
			}
			return nil
		}).Count(&count); d.Error != nil {
			return users, count, d.Error
		}
	} else if len(whereMap) != 0 {
		if d := variable.DB.MySQL.Model(&User{}).Where(whereMap).FindInBatches(&users, pageSize, func(tx *gorm.DB, batch int) error {
			for k, v := range users {
				var agent Agent
				var roles Roles
				if d := variable.DB.MySQL.Model(&Agent{}).Where("id = ?", v.AgentID).Find(&v.Agent); d.Error != nil {
					return d.Error
				}
				users[k].Agent = agent
				if d := variable.DB.MySQL.Model(&Roles{}).Where("id = ?", v.RoleId).Find(&v.Roles); d.Error != nil {
					return d.Error
				}
				users[k].Roles = roles
			}
			return nil
		}).Count(&count); d.Error != nil {
			return users, count, d.Error
		}
	} else if len(whereStr) != 0 {
		if d := variable.DB.MySQL.Model(&User{}).Where(whereStr).FindInBatches(&users, pageSize, func(tx *gorm.DB, batch int) error {
			for k, v := range users {
				var agent Agent
				var roles Roles
				if d := variable.DB.MySQL.Model(&Agent{}).Where("id = ?", v.AgentID).Find(&v.Agent); d.Error != nil {
					return d.Error
				}
				users[k].Agent = agent
				if d := variable.DB.MySQL.Model(&Roles{}).Where("id = ?", v.RoleId).Find(&v.Roles); d.Error != nil {
					return d.Error
				}
				users[k].Roles = roles
			}
			return nil
		}).Count(&count); d.Error != nil {
			return users, count, d.Error
		}
	}
	return users, count, nil
}

type UserLoginLog struct {
	Model
	UserID uint
	User   User
	Type   string `gorm:"type:varchar(50);comment:用户打开小程序类型-login/open"`
}

func (ulg *UserLoginLog) TableName() string {
	return "user_login_log"
}

type UserShareLog struct {
	Model
	UserID         uint
	User           User
	ShareTimeStamp uint
	ClickCount     uint `gorm:"comment:本次分享一共有多少人点击"`
}

func (usl *UserShareLog) TableName() string {
	return "user_share_log"
}
